package com.bocloud.cmp.server.app.scheduler;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import com.alibaba.fastjson.JSONArray;
import com.bocloud.cmp.dao.app.RegistryDao;
import com.bocloud.cmp.dao.app.RegistryImageInfoDao;
import com.bocloud.cmp.dao.app.RegistrySoftwareDao;
import com.bocloud.cmp.dao.app.SoftwareDao;
import com.bocloud.cmp.entity.app.resource.Registry;
import com.bocloud.cmp.entity.app.resource.RegistryImageInfo;
import com.bocloud.cmp.entity.app.resource.RegistrySoftware;
import com.bocloud.cmp.entity.app.resource.Software;
import com.bocloud.cmp.enums.app.CommonEnum.Status;
import com.bocloud.cmp.service.utils.RegistryUtil;
import com.bocloud.common.http.ExtendHttpClient;
import com.bocloud.common.model.BaseResult;
import com.bocloud.common.model.BsmResult;
import com.bocloud.common.utils.ListTool;
import com.bocloud.coordinator.cache.LeaderShip;
import com.bocloud.soc.dao.intf.DepartmentDao;
import com.bocloud.soc.entity.Department;

/**
 * 本地仓库与服务端仓库镜像信息同步
 * 
 * @author Zaney
 * @data:2017年3月14日
 * @describe:
 */
@Component("system.properties")
public class ImageScheduler {
	private static Logger logger = LoggerFactory.getLogger(ImageScheduler.class);
	@Autowired
	private RegistryDao registryDao;
	@Autowired
	private RegistryImageInfoDao registryImageInfoDao;
	@Autowired
	private SoftwareDao softwareDao;
	@Autowired
	private RegistrySoftwareDao registrySoftwareDao;
	@Autowired
	private DepartmentDao departmentDao;

	
	@SuppressWarnings({ "rawtypes", "unchecked" })
	@Scheduled(cron = "${sync.schedule}")
	public void sync() {
		try {
			if (LeaderShip.isLeader()) {
				// 1、获取所有的仓库
				List<Registry> registrys = registryDao.selectAllRegistry();
				for (Registry registry : registrys) {
					// 如果是锁定状态不允许操作
					if (Integer.parseInt(registry.getStatus()) == Status.LOCK.ordinal()) {
						continue;
					}
					// 2、获取服务器端仓库的镜像
					BaseResult serverImagesResult = getServerImages(registry.getId());
					if (serverImagesResult.isSuccess()) {
						List<Software> serverImages = (List<Software>) serverImagesResult.getData();
						if (serverImages.isEmpty()) {
							logger.info("服务器仓库不存在任何镜像信息");
						}
						// 3、获取数据库中仓库镜像表信息
						List<RegistryImageInfo> repositorySoftwares = registryImageInfoDao
								.selectRepositorySoftware(registry.getId());
						if (repositorySoftwares.isEmpty()) {
							logger.info("数据库仓库不存在任何镜像信息");
						}
						// 4、比对服务端与本地端镜像信息
						BsmResult imageInfoCompare = imageInfoCompare(serverImages, repositorySoftwares,
								registry.getId());
						if (imageInfoCompare.isSuccess()) {
							logger.info("信息同步成功");
						}
					}
				}
			}
		} catch (Exception e) {
			logger.error("信息同步异常", e);
		}
	}

	/**
	 * 服务端镜像与本地端镜像信息比对
	 * 
	 * @param serverImageInfos
	 * @param localImageInfos
	 * @return
	 */
	public BsmResult imageInfoCompare(List<Software> serverImages, List<RegistryImageInfo> localSoftwares, Long id) {
		StringBuffer serverName = null;
		StringBuffer localName = null;
		try {
			// 收集服务端 镜像:版本号
			List<String> serverImageList = new ArrayList<String>();
			// 收集本地端 镜像:版本号
			List<String> localSoftwareList = new ArrayList<String>();
			for (Software serverImage : serverImages) {
				serverName = new StringBuffer();
				serverName.append(serverImage.getName()).append(":").append(serverImage.getVersion());
				serverImageList.add(serverName.toString());
			}
			for (RegistryImageInfo localSoftware : localSoftwares) {
				localName = new StringBuffer();
				localName.append(localSoftware.getNamespace()).append("/").append(localSoftware.getName()).append(":")
						.append(localSoftware.getVersion());
				localSoftwareList.add(localName.toString());
			}
			// 1、服务器端仓库 与 数据库中仓库 的镜像信息进行比对 添加操作
			for (Software serverImage : serverImages) {
				serverName = new StringBuffer();
				serverName.append(serverImage.getName()).append(":").append(serverImage.getVersion());
				if (!localSoftwareList.contains(serverName.toString())) {
					String namespace = serverImage.getName().split("/")[0];
					// 根据namespace获取组织id
					List<Department> list = departmentDao.list(Department.class,
							"select * from department where is_deleted = 0 and name = '" + namespace + "'");
					if (!ListTool.isEmpty(list)) {
						Long deptId = list.get(0).getId();
						serverImage.setDeptId(deptId);
					}
					// 镜像名测拆分和重组
					String name = serverImage.getName().substring(serverImage.getName().indexOf("/") + 1);
					serverImage.setName(name);
					serverImage.setDeleted(false);
					serverImage.setProperty(0);
					serverImage.setType(0);
					serverImage.setUsage_count(0);
					serverImage.setGmtCreate(new Date());
					serverImage.setMenderId(1L);
					serverImage.setCreaterId(1L);
					serverImage.setStatus(String.valueOf(Status.NORMAL.ordinal()));
					softwareDao.save(serverImage);
					// 维护仓库镜像关联表
					RegistrySoftware registrySoftware = new RegistrySoftware();
					registrySoftware.setNamespace(namespace);
					registrySoftware.setRepositoryId(id);
					registrySoftware.setSoftwareId(serverImage.getId());
					registrySoftwareDao.saveRegistrySoftware(registrySoftware);
				}
			}

			// 2、数据库中仓库 与 服务器端仓库 的镜像信息进行比对 删除操作
			for (RegistryImageInfo localSoftware : localSoftwares) {
				localName = new StringBuffer();
				localName.append(localSoftware.getNamespace()).append("/").append(localSoftware.getName()).append(":")
						.append(localSoftware.getVersion());
				if (!serverImageList.contains(localName.toString())) {
					// 删除镜像
					softwareDao.remove(Software.class, localSoftware.getId());
					// 删除仓库镜像表关系
					List<RegistrySoftware> registrySoftware = registrySoftwareDao
							.getRegistrySoftwareById(localSoftware.getRegId());
					if (ListTool.isEmpty(registrySoftware)) {
						logger.debug("仓库镜像关联表信息存储异常，获取的信息失败:" + registrySoftware);
						return new BsmResult(false, "仓库镜像关联表信息存储异常，获取的信息失败:" + registrySoftware);
					}
					registrySoftwareDao.deleteRegistrySoftware(registrySoftware.get(0));
				}
			}
			return new BsmResult(true, "同步成功");
		} catch (Exception e) {
			logger.error("同步信息出现异常", e);
			return new BsmResult(false, "同步异常");
		}
	}

	/**
	 * 获取服务端镜像信息
	 * 
	 * @param id
	 * @return
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public BaseResult getServerImages(Long repositoryId) {
		List<Software> RegistryImageInfoList = new ArrayList<Software>();
		try {
			// 0、获取仓库信息
			Registry registry = registryDao.query(repositoryId);
			if (registry == null) {
				logger.info("仓库不存在");
				return new BaseResult(false, "仓库不存在");
			}
			String url = registry.getAddress() + ":" + registry.getRegistryPort();

			ExtendHttpClient extendHttpClient = new ExtendHttpClient();
			// 1、获取仓库中所有镜像的名称
			BaseResult imageNamesResult = RegistryUtil.getImageNames(url);
			if (!imageNamesResult.isSuccess()) {
				return new BaseResult(false, "连接仓库[" + registry.getAddress() + "]出现异常，获取仓库镜像名称信息失败");
			}
			List<String> imageNames = (List<String>) imageNamesResult.getData();
			// 2、遍历镜像名称 获取imageName+ImagetTag 放入集合
			for (String imageName : imageNames) {
				// 获取每个镜像下对应的tag集合
				BaseResult imageTagsResult = RegistryUtil.getImageTags(url, imageName);
				if (!imageTagsResult.isSuccess()) {
					logger.info("连接仓库[" + registry.getAddress() + "]出现异常，获取仓库镜像[" + imageName + "]的tag失败");
					return new BaseResult(false,
							"连接仓库[" + registry.getAddress() + "]出现异常，获取仓库镜像[" + imageName + "]的tag失败");
				}
				JSONArray imageTags = (JSONArray) imageTagsResult.getData();
				// 判断 若该镜像下所有的tag为null 则进行下一次循环
				if (ListTool.isEmpty(imageTags)) {
					continue;
				}
				// 循环遍历镜像tag
				for (Object tag : imageTags) {
					Software software = new Software();
					software.setName(imageName);
					software.setVersion(tag.toString());
					// 获取镜像id
					BsmResult digestsResult = RegistryUtil.getSoftwareDigestByNameAndVersion(registry.getAddress(),
							registry.getRegistryPort(), imageName, tag.toString(), extendHttpClient);
					if (!digestsResult.isSuccess()) {
						logger.warn("连接仓库[" + registry.getAddress() + "]出现异常，获取仓库镜像[" + imageName + "]的id失败");
						continue;
					} 
					software.setUuid(digestsResult.getData().toString());
					RegistryImageInfoList.add(software);
				}
			}
			return new BaseResult(true, "查询成功", RegistryImageInfoList);
		} catch (Exception e) {
			logger.error("get registry  fail:", e);
			return new BaseResult(false, "获取仓库镜像信息异常");
		}
	}

}
